package com.jzx.provider.cv.service;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.hankcs.hanlp.HanLP;
import com.jzx.provider.common.utils.DateUtil;
import com.jzx.provider.cv.constants.Education;
import com.jzx.provider.cv.dto.CvPage;
import com.jzx.provider.cv.dto.query.CvQueryForIndex;
import com.jzx.provider.cv.dto.result.CvResultForIndex;
import com.jzx.provider.cv.dto.result.EduResultForIndex;
import com.jzx.provider.cv.dto.result.WorkResultForIndex;
import com.jzx.provider.cv.model.*;
import com.jzx.provider.cv.repository.*;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
import org.joda.time.Months;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Transactional(rollbackFor = Exception.class)
public class CvIndexService {
    @Autowired
    private CvRepository cvRepository;

    @Autowired
    private CvIndexRepository cvIndexRepository;

    @Autowired
    private CurrentInfoRepository currentInfoRepository;

    @Autowired
    private ForwardInfoRepository forwardInfoRepository;

    @Autowired
    private EducationInfoRepository educationInfoRepository;

    @Autowired
    private WorkInfoRepository workInfoRepository;

    @Autowired
    private ProjectInfoRepository projectInfoRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private UniversityRepository universityRepository;

    @Autowired
    private ComNameRepository comNameRepository;

    /**
     * 创建简历索引
     */
    public void createIndex(int count) {
        List<IndexQuery> queries = new ArrayList<IndexQuery>();
        cvRepository.getCvByPage(count).forEach(cv -> {
            CvIndex cvIndex = CvIndex.builder().build();
            //简历关键字
            Set<String> keys = new HashSet<>();
            BeanCopier.create(Cv.class, CvIndex.class, false)
                    .copy(cv, cvIndex, null);
            cvIndex.setId(cv.getId() + "");
            //目前状况索引
            CurrentInfo currentInfo = currentInfoRepository.findByCvId(cv.getId());
            if (currentInfo != null) {
                cvIndex.setNowLocation(currentInfo.getNowLocation());
                cvIndex.setSwitchStatus(currentInfo.getSwitchStatus());
            }
            //期望状况索引
            ForwardInfo forwardInfo = forwardInfoRepository.findByCvId(cv.getId());
            if (forwardInfo != null) {
                cvIndex.setForwardCities(forwardInfo.getCities());
                //cvIndex.setForwardSalary(forwardInfo.getSalary());
            }
            //学历索引
            List<EducationInfo> educationInfos = educationInfoRepository.findByCvId(cv.getId());
            if (educationInfos != null && educationInfos.size() > 0) {
                //最高学历
                cvIndex.setEdu(educationInfos.get(0).getEducation());
                cvIndex.setIs211Or985ForUp(educationInfos.get(0).getIsKeySchool());
                cvIndex.setEducationInfos("");
                educationInfos.forEach(educationInfo -> {
                    //第一学历
                    if (educationInfo.getFirstOrNot() != null && educationInfo.getFirstOrNot() == 1) {
                        cvIndex.setFirstEdu(educationInfo.getEducation());
                        cvIndex.setIs211Or985ForFirst(educationInfo.getIsKeySchool());
                    }
                    //组建教育信息
                    String eduItem = educationInfo.getStartDate() + "," + educationInfo.getEndDate() + "," + educationInfo.getSchool() + "," + Education.getByValue(educationInfo.getEducation()) + "," + educationInfo.getMajor();
                    cvIndex.setEducationInfos(cvIndex.getEducationInfos() + eduItem + ";");
                });
            }
            //工作经历索引
            List<WorkInfo> workInfos = workInfoRepository.findByCvId(cv.getId());
            if (workInfos != null && workInfos.size() > 0) {
                cvIndex.setNowVocation(workInfos.get(0).getVocation());
                cvIndex.setNowTitle(workInfos.get(0).getTitle());
                cvIndex.setWorkInfos("");
                cvIndex.setNowCompany(workInfos.get(0).getCompany());
                cvIndex.setNowTitle(workInfos.get(0).getTitle());
                //计算开始工作日期
                cvIndex.setStartWorkDate(workInfos.get(workInfos.size() - 1).getStartDate());
                workInfos.forEach(workInfo -> {
                    //组件工作经历
                    String workItem = workInfo.getStartDate() + "," + workInfo.getEndDate() + "," + workInfo.getCompany() + "," + workInfo.getTitle();
                    cvIndex.setWorkInfos(cvIndex.getWorkInfos() + workItem + ";");
                    if (!StringUtils.isEmpty(workInfo.getDuties())) {
                        HanLP.segment(workInfo.getDuties().toLowerCase()).forEach(item -> {
                            if (item.nature.toString().equals("key") || item.nature.toString().equals("skill")) {
                                keys.add(item.word);
                            }
                        });
                    }
                });
            }

            projectInfoRepository.findByCvId(cv.getId()).forEach(projectInfo -> {
                if (!StringUtils.isEmpty(projectInfo.getDuties())) {
                    HanLP.segment(projectInfo.getDuties().toLowerCase()).forEach(item -> {
                        if (item.nature.toString().equals("key") || item.nature.toString().equals("skill")) {
                            keys.add(item.word);
                        }
                    });
                }
            });

            if (!StringUtils.isEmpty(cv.getProfessional())) {
                HanLP.segment(cv.getProfessional().toLowerCase()).forEach(item -> {
                    if (item.nature.toString().equals("key") || item.nature.toString().equals("skill")) {
                        keys.add(item.word);
                    }
                });
            }
            cvIndex.setCvKeywords(String.join(",", keys));
            IndexQuery indexQuery = new IndexQuery();
            Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
            indexQuery.setId(cv.getId().toString());
            indexQuery.setSource(gson.toJson(cvIndex));
            indexQuery.setIndexName("cv");
            indexQuery.setType("cv");
            queries.add(indexQuery);
            cv.setIsIndex(1);
            cvRepository.save(cv);
        });
        if (queries.size() > 0) {
            elasticsearchTemplate.bulkIndex(queries);
            elasticsearchTemplate.refresh(CvIndex.class);
        }
    }

    /**
     * 简历检索
     */
    public CvPage<CvResultForIndex> findCvIndexByQuery(CvQueryForIndex cvQueryForIndex) {
        BoolQueryBuilder builder = QueryBuilders.boolQuery();
        //记录关键字
        Set<String> highlight = new HashSet<>();
        //关键字
        keyWordsQuery(cvQueryForIndex.getKeys(), builder, highlight);
        //性别
        if (cvQueryForIndex.getSex() != null) {
            builder.must(QueryBuilders.termQuery("sex", cvQueryForIndex.getSex()));
        }
        //学历
        if (cvQueryForIndex.getEducation() != null) {
            builder.must(QueryBuilders.rangeQuery("edu").gte(cvQueryForIndex.getEducation()));
        }
        if (cvQueryForIndex.getEducationFirst() != null && cvQueryForIndex.getEducationFirst()) {
            builder.must(QueryBuilders.termQuery("firstEdu", 2));
        }
        if ((!StringUtils.isEmpty(cvQueryForIndex.getIs211()) && cvQueryForIndex.getIs211()) &&
                (!StringUtils.isEmpty(cvQueryForIndex.getIs985()) && cvQueryForIndex.getIs985())
        ) {
            builder.must(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("is211Or985ForUp", 1))
                    .should(QueryBuilders.matchQuery("is211Or985ForUp", 2)));
        } else if (!(!StringUtils.isEmpty(cvQueryForIndex.getIs211()) && cvQueryForIndex.getIs211()) &&
                (!StringUtils.isEmpty(cvQueryForIndex.getIs985()) && cvQueryForIndex.getIs985())) {
            builder.must(QueryBuilders.matchQuery("is211Or985ForUp", 2));
        } else if ((!StringUtils.isEmpty(cvQueryForIndex.getIs211()) && cvQueryForIndex.getIs211()) &&
                !(!StringUtils.isEmpty(cvQueryForIndex.getIs985()) && cvQueryForIndex.getIs985())) {
            builder.must(QueryBuilders.matchQuery("is211Or985ForUp", 1));
        }
        //所在公司
        if (!StringUtils.isEmpty(cvQueryForIndex.getCompany())) {
            //判断是当前公司还是所有工作经历
            if (cvQueryForIndex.getCompanySelect().equals(1)) {
                builder.must(QueryBuilders.matchPhraseQuery("nowCompany", cvQueryForIndex.getCompany()));
            } else if (cvQueryForIndex.getCompanySelect().equals(2)) {
                builder.must(QueryBuilders.matchPhraseQuery("workInfos", cvQueryForIndex.getCompany()));
            }
            highlight.add(cvQueryForIndex.getCompany().trim());
        }
        //职位
        if (!StringUtils.isEmpty(cvQueryForIndex.getTitle())) {
            builder.must(QueryBuilders.matchPhraseQuery("workInfos", cvQueryForIndex.getTitle()));
            highlight.add(cvQueryForIndex.getTitle());
        }
        //期望城市
        if (!StringUtils.isEmpty(cvQueryForIndex.getForwardCity())) {
            builder.must(QueryBuilders.termQuery("forwardCities", cvQueryForIndex.getForwardCity()));
        }
        //工作经验
        workYears(cvQueryForIndex.getWorkYear(), builder);
        //行业
        if (cvQueryForIndex.getVocation() != null && cvQueryForIndex.getVocation().size() > 0) {
            List<String> res = new ArrayList<>();
            for (List<String> list : cvQueryForIndex.getVocation()) {
                list.remove(0);
                res.add(String.join(",", list));
            }
            builder.must(QueryBuilders.matchPhraseQuery("nowVocation", String.join(",", res)));
        }
        //年龄
        age(cvQueryForIndex.getAgeOne(), cvQueryForIndex.getAgeTwo(), builder);
        //职业状态
        if (cvQueryForIndex.getSwitchStatus() != null) {
            builder.must(QueryBuilders.matchPhraseQuery("switchStatus", cvQueryForIndex.getSwitchStatus()));
        }
        //分页排序
        PageRequest page = new PageRequest(cvQueryForIndex.getPageNum() - 1, cvQueryForIndex.getPageSize());
        FieldSortBuilder sort = SortBuilders.fieldSort("updateTime").order(SortOrder.DESC);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(builder)
                .withSort(sort)
                .withPageable(page)
                .build();
        Page<CvIndex> paperIndexPage = cvIndexRepository.search(searchQuery);
        List<CvResultForIndex> cvResultForIndices = new ArrayList<>();
        paperIndexPage.getContent().forEach(item -> {
            CvResultForIndex cvResultForIndex = CvResultForIndex.builder().build();
            BeanCopier.create(CvIndex.class, CvResultForIndex.class, false)
                    .copy(item, cvResultForIndex, null);
            cvResultForIndex.setEducation(Education.getByValue(item.getEdu()));
            //计算年龄
            if (item.getBirthDay() != null) {
                try {
                    cvResultForIndex.setAge(DateUtil.getYearByDate(item.getBirthDay()));
                } catch (Exception e) {
                }
            }
            //计算工作年
            if (!StringUtils.isEmpty(item.getStartWorkDate())) {
                cvResultForIndex.setWorkYear(DateUtil.getWorkYear(item.getStartWorkDate()));
            }
            //性别
            cvResultForIndex.setSex(item.getSex() == 1 ? "男" : "女");
            if (!StringUtils.isEmpty(item.getEducationInfos())) {
                for (String key : highlight) {
                    item.setEducationInfos(IgnoreCaseReplace(item.getEducationInfos(), key));
                }
                String[] educationInfos = item.getEducationInfos().split(";");
                List<EduResultForIndex> eduResultForIndices = new ArrayList<>();
                for (String educationInfo : educationInfos) {
                    String[] eduInfo = educationInfo.split(",", 5);
                    if (eduInfo.length == 5) {
                        EduResultForIndex eduResultForIndex = EduResultForIndex.builder()
                                .startDate(eduInfo[0])
                                .endDate(eduInfo[1])
                                .school(eduInfo[2])
                                .edu(eduInfo[3])
                                .build();
                        eduResultForIndices.add(eduResultForIndex);
                    }
                }
                cvResultForIndex.setEdus(eduResultForIndices);
            }
            if (!StringUtils.isEmpty(item.getWorkInfos())) {
                for (String key : highlight) {
                    item.setWorkInfos(IgnoreCaseReplace(item.getWorkInfos(), key));
                }
                String[] workInfos = item.getWorkInfos().split(";");
                List<WorkResultForIndex> workResultForIndices = new ArrayList<>();
                for (String workInfo : workInfos) {
                    String[] work = workInfo.split(",", 4);
                    if (work.length == 4) {
                        WorkResultForIndex workResultForIndex = WorkResultForIndex.builder()
                                .startDate(work[0])
                                .endDate(work[1])
                                .company(work[2])
                                .title(work[3])
                                .build();
                        //计算工作时长（月）
                        DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM");
                        DateTime start = DateTime.now();
                        DateTime end = DateTime.now();
                        if (!work[0].equals("至今")) {
                            start = formatter.parseDateTime(work[0]);
                        }
                        if (!work[1].equals("至今")) {
                            end = formatter.parseDateTime(work[1]);
                        }
                        int months = Months.monthsBetween(start, end).getMonths();
                        if (months < 12) {
                            workResultForIndex.setWorkTimes(months % 12 + "个月");
                        } else {
                            workResultForIndex.setWorkTimes((months - months % 12) / 12 + "年" + months % 12 + "个月");
                        }

                        workResultForIndices.add(workResultForIndex);
                    }
                }
                cvResultForIndex.setWorks(workResultForIndices);
            }
            cvResultForIndices.add(cvResultForIndex);
        });
        return CvPage.<CvResultForIndex>builder()
                .content(cvResultForIndices)
                .total(paperIndexPage.getTotalElements())
                .highlight(highlight)
                .build();
    }

    /**
     * 不区分大小写高亮替换
     *
     * @param source
     * @param patternstring
     * @return
     */
    public String IgnoreCaseReplace(String source, String patternstring) {
        Pattern p = Pattern.compile(patternstring, Pattern.CASE_INSENSITIVE);
        Matcher mc = p.matcher(source);
        StringBuffer sb = new StringBuffer();
        while (mc.find()) {
            mc.appendReplacement(sb, "<font color='red' >" + mc.group() + "</font>");
        }
        mc.appendTail(sb);
        return sb.toString();
    }

    /**
     * 处理关键字搜索
     */
    private void keyWordsQuery(String keys, BoolQueryBuilder builder, Set highlight) {
        //关键字搜索
        if (!StringUtils.isEmpty(keys)) {
            HanLP.segment(keys.toLowerCase()).forEach(term -> {
                //学校或者专业
                if (term.nature.toString().trim().startsWith("school") || term.nature.toString().equals("major")) {
                    //判断是否为简称
                    University university = universityRepository.findByNameOrShortName(term.word.toLowerCase().trim(), term.word.toLowerCase().trim());
                    if (university != null && !StringUtils.isEmpty(university.getShortName())) {
                        highlight.add(university.getShortName());
                        highlight.add(university.getName());
                        builder.must(QueryBuilders.boolQuery().should(QueryBuilders.matchPhraseQuery("educationInfos", university.getName()))
                                .should(QueryBuilders.matchPhraseQuery("educationInfos", university.getShortName())));
                    } else {
                        highlight.add(term.word);
                        builder.must(QueryBuilders.matchPhraseQuery("educationInfos", term.word));
                    }
                } else if (term.nature.toString().equals("title")) {
                    builder.must(QueryBuilders.matchPhraseQuery("workInfos", term.word));
                    highlight.add(term.word);
                } else if (term.nature.toString().equals("com")) {
                    ComName comName = comNameRepository.findByNameOrShortName(term.word.toLowerCase().trim(), term.word.toLowerCase().trim());
                    if (comName != null) {
                        highlight.add(comName.getName());
                        highlight.add(comName.getShortName());
                        builder.must(QueryBuilders.boolQuery().should(QueryBuilders.matchPhraseQuery("workInfos", comName.getName()))
                                .should(QueryBuilders.matchPhraseQuery("workInfos", comName.getShortName())));
                    }
                } else if (term.nature.toString().equals("key") || term.nature.toString().equals("skill")) {
                    builder.must(QueryBuilders.boolQuery().should(QueryBuilders.matchPhraseQuery("cvKeywords", term.word))
                            .should(QueryBuilders.matchPhraseQuery("workInfos", term.word)));
                    highlight.add(term.word);
                } else if (term.nature.toString().equals("sex")) {
                    builder.must(QueryBuilders.termQuery("sex", term.word == "男" ? 1 : 2));
                }
            });
        }
    }

    /**
     * 处理工作经验
     */
    private void workYears(Integer workYears, BoolQueryBuilder builder) {
        if (workYears != null) {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Calendar calendarStart = Calendar.getInstance();
            Calendar calendarEnd = Calendar.getInstance();
            //1年以下
            if (workYears.equals(1)) {
                calendarStart.setTime(new Date());
                calendarStart.add(Calendar.YEAR, -1);
                calendarEnd.setTime(new Date());
            } else if (workYears.equals(2)) {//1-3年
                calendarStart.setTime(new Date());
                calendarStart.add(Calendar.YEAR, -3);
                calendarEnd.setTime(new Date());
                calendarEnd.add(Calendar.YEAR, -1);
            } else if (workYears.equals(3)) {//3-5年
                calendarStart.setTime(new Date());
                calendarStart.add(Calendar.YEAR, -5);
                calendarEnd.setTime(new Date());
                calendarEnd.add(Calendar.YEAR, -3);
            } else if (workYears.equals(4)) {//5-10年
                calendarStart.setTime(new Date());
                calendarStart.add(Calendar.YEAR, -10);
                calendarEnd.setTime(new Date());
                calendarEnd.add(Calendar.YEAR, -5);
            } else if (workYears.equals(5)) {//10年以上
                calendarStart.setTime(new Date());
                calendarStart.add(Calendar.YEAR, -100);
                calendarEnd.setTime(new Date());
                calendarEnd.add(Calendar.YEAR, -10);
            }
            builder.must(QueryBuilders.rangeQuery("startWorkDate").gt(dateFormat.format(calendarStart.getTime()) + " 00:00:00")
                    .lt(dateFormat.format(calendarEnd.getTime()) + " 00:00:00"));
        }
    }

    /**
     * 处理年龄
     */
    public void age(Integer ageOne, Integer ageTwo, BoolQueryBuilder builder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        if (ageOne != null) {
            Calendar calendarOne = Calendar.getInstance();
            calendarOne.setTime(new Date());
            calendarOne.add(Calendar.YEAR, 0 - ageOne);
            builder.must(QueryBuilders.rangeQuery("birthDay").lte(dateFormat.format(calendarOne.getTime()) + " 00:00:00"));
        }

        if (ageTwo != null) {
            Calendar calendarTwo = Calendar.getInstance();
            calendarTwo.setTime(new Date());
            calendarTwo.add(Calendar.YEAR, 0 - ageTwo);
            builder.must(QueryBuilders.rangeQuery("birthDay").gte(dateFormat.format(calendarTwo.getTime()) + " 00:00:00"));
        }
    }
}
